package com.mianshi;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;

public class CompileClassLoader extends ClassLoader {

    String path="E:/java学习/设计模式/mianshi";

    //读取一个文件的内容
    private byte[] getBytes(String fileName) throws IOException{
        File file=new File(fileName);
        long len=file.length();
        byte[] raw=new byte[(int) len];

        FileInputStream fin=new FileInputStream(file);
        //一次读取class文件的全部二进制数据
        int r=fin.read(raw);
        if(r!=len){
            throw new IOException("无法读取全部文件："+r+"!="+len);
        }
        fin.close();

        return raw;
    }

    //定义编译指定JAVA文件的方法
    private boolean compile(String javaFile) throws IOException{
        System.out.println("CompileClassLoader:正在编译"+javaFile+"...");
        //调用系统的javac命令
        Process p=Runtime.getRuntime().exec("javac "+javaFile);
        try {
            //其他线程都等待这个线程完成
            p.waitFor();
        } catch (Exception e) {
            System.out.println(e);
        }
        //获取javac线程的退出值
        int ret=p.exitValue();//此 Process 对象表示的子进程的出口值。根据惯例，值 0 表示正常终止。
        //返回编译是否成功
        return ret==0;
    }

    //重写ClassLoader的findClass方法
    protected Class<?> findClass(String name) {
        Class clazz=null;
        //将包路径中的点(.)替换成斜线(/)
        String fileSub=path+name.replace(".", "/");

        String javaFilename=fileSub+".java";
        String classFilename=fileSub+".class";

        File javaFile=new File(javaFilename);
        File classFile=new File(classFilename);

        //当指定的Java源文件存在，且class文件不存在；或Java 源文件的修改时间比class文件的修改时间晚时，重新编译
        if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){
            //如果编译失败，或者该class文件不存在
            try {
                if (!compile(javaFilename) || !classFile.exists()) {
                    throw new ClassNotFoundException("ClassNotFoundException:"+javaFilename);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //如果class文件存在，系统负责将该文件转换成Class对象
        if(classFile.exists()){
            try {
                //将class文件的二进制数据读入数组
                byte[] raw=getBytes(classFilename);
                //调用classLoader的defineClass方法将二进制数据转换成Class对象
                clazz=defineClass(name, raw, 0,raw.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //如果clazz为null，表明加载失败，则抛出异常
        if(clazz==null){
            try {
                throw new ClassNotFoundException(name);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return clazz;
    }


    //定义一个主方法
    public static void main(String[] args) throws Exception {
        //如果运行该程序时没有参数，即没有目标类
        if(args.length<1){
            System.out.println("缺少运行的目标类，请按如下格式运行Java源文件:");
            System.out.println("类的全路径名   参数,如： com.reflect.Hello  参数1");
        }
        //第一个参数是需要运行的类
        String progClass=args[0];
        //剩下的参数作为运行目标类时的参数，所以将这些参数复制到一个新数组中
        String progArgs[]=new String[args.length-1];
        System.arraycopy(args, 1, progArgs, 0, progArgs.length);
        CompileClassLoader ccl=new CompileClassLoader();
        //加载需要运行的类
        Class<?> clazz=ccl.loadClass(progClass);
        //获取需要运行类的主方法
        Method main=clazz.getMethod("main", (new String[0]).getClass());

        Object argsArray[]={progArgs};

        main.invoke(null, argsArray);
    }

}